﻿#include "businessfile.h"
#include <protocol/protocolConverter.h>
#include "businessnetwork.h"
#include <SyncManager/syncmanager.h>
#include "businesssql.h"
#include <Debug/DEBUG.hpp>
#include <UserManager/usermanager.h>
#include <IdGenerator/idgenerator.h>
#include <FileManager/filemanager.h>
#include <CipherUtils/cipherutils.h>
#include "businesssql.h"

BusinessFile::BusinessFile() {}

void BusinessFile::recvDownloadRequest(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel)
{
    // check validation
    // default: success
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());

    uint64_t syncPoint = std::get<int64_t>(converter.getHeaderField(4));
    uint64_t fileType = std::get<int64_t>(converter.getHeaderField(3));
    uint64_t fileID = std::get<int64_t>(converter.getHeaderField(2));
    uint64_t userID = BusinessNetWork::getInstance().getUserID(channel);

    // 如果正在有一个文件向客户端传输，则拒绝传输请求
    if (checkUpload(userID)) {
        BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                           4, 4, 0, 0, syncPoint,
                                                           nullptr, 0);
        return;
    }

    BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                       4, 3, 0, 0, syncPoint,
                                                       nullptr, 0);

    // get file info
    nlohmann::json fileJson;
    std::string filePath;
    if (fileType == 1) {
        fileJson = BusinessSQL::getInstance().queryAFile(fileID);
        filePath = "./files/" + (std::string)fileJson["FileName"];
    }
    else {
        fileJson = BusinessSQL::getInstance().queryAPicture(std::to_string(fileID));
        filePath = "./picture/" + (std::string)fileJson["FileName"];
    }

    /// do uploadFile() of client

    // get filePath and fileSize

    uint64_t fileSize = fileJson["FileSize"];

    DEBUG_INFO((std::string)"send file info: " + (std::string)"FileName: " + (std::string)fileJson["FileName"] + " FileSize: " + std::to_string(fileSize));
    SyncManager & syncManager = SyncManager::getInstance();
    BusinessNetWork& net = BusinessNetWork::getInstance();
    AppConfig& config = AppConfig::getInstance();
    // build apply message
    nlohmann::json messageJson;
    messageJson["FileName"] = fileJson["FileName"];
    messageJson["FileSize"] = fileSize;
    messageJson["FileType"] = fileType;
    std::string messageStr = messageJson.dump();
    // apply for a unique id
    IDGenerator idgenerator(std::stoi(config.globalValue("MechineID")));
    uint64_t id = idgenerator.generate();
    DEBUG_INFO("file id: " + std::to_string(id));
    // send to client
    SyncPoint point = syncManager.generateUniqueSyncPoint();
    net.sendMessageToClient(channel,
                            3, 1, 0, id, point,
                            (unsigned char *)messageStr.data(), messageStr.size());
    // wait for client reply
    // 更新文件上传的同步点
    updateUploadSyncPoint(userID, point);
    bool res = syncManager.waitForSyncPoint(point);

    if (!res) {
        DEBUG_INFO("transmission rejected");
        // transmission rejected
        return ;
    }
    // transmission accept


    FileManager& fileManager = FileManager::getInstance();
    CipherUtils& utils = CipherUtils::getInstance();

    uint64_t fileBlocks = fileSize / fileManager.FILE_BLOCK_SIZE;
    // send every block of file
    int mode = std::stoi(config.globalValue("TransmissionMode"));

    for (uint64_t i = 0; i <= fileBlocks; i ++) {
        // 如果当前已经没有文件要传输了，则停止传输
        if (!checkUpload(userID))
            return;
        nlohmann::json messageToSend;
        uint64_t blockOffset = i;
        std::pair<std::shared_ptr<unsigned char[]>, int> files;
        if (mode == 1) {
            files = fileManager.readFromDisk(filePath, blockOffset, fileManager.FILE_BLOCK_SIZE, false);
        } else {
            files = fileManager.readFromDisk(filePath, blockOffset, fileManager.ENCRYPTED_FILE_BLOCK_SIZE, true);
        }

        std::string fileBase64 = utils.Base64_Encode(files.first.get(), files.second);

        messageToSend["TransmissionID"] = id;
        messageToSend["BlockOffset"] = i;
        messageToSend["FileBase64"] = fileBase64;
        messageToSend["BlockSize"] = files.second;

        std::string messageStr = messageToSend.dump();
        SyncPoint filePoint = syncManager.generateUniqueSyncPoint();

        net.sendMessageToClient(channel,
                                3, 3, id, 0, filePoint,
                                (unsigned char *)messageStr.data(), messageStr.size());
        // 更新文件上传的同步点
        updateUploadSyncPoint(userID, filePoint);
        // wait for handl success
        syncManager.waitForSyncPoint(filePoint);
    }

    net.sendMessageToClient(channel,
                            3, 4, 0, 0, id,
                            nullptr, 0);
    // 文件上传结束，更新当前用户的状态信息
    updateUploadSyncPoint(userID, -1);
}

void BusinessFile::RecvConfirmOfFileBlock(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel)
{
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char *)protocol->data());

    SyncPoint syncPoint = std::get<int64_t>(converter.getHeaderField(4));
    SyncManager& manager = SyncManager::getInstance();
    manager.releaseSyncPoint(syncPoint, true);
}

void BusinessFile::sendFileList(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel)
{
    nlohmann::json files = BusinessSQL::getInstance().queryAllTheFile();
    std::string fileStr = files.dump();
    BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                       6, 3, 0, 0, 0,
                                                       (unsigned char *)fileStr.c_str(), fileStr.size());
}

void BusinessFile::recvUploadFileRequest(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel)
{
    nlohmann::json fileInfo = getJsonOfMessage(protocol);
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());

    uint64_t id = std::get<int64_t>(converter.getHeaderField(3));
    int type = fileInfo["FileType"];
    std::string path;
    if (type == 1) {
        path = "./files/" + (std::string)fileInfo["FileName"];
    } else {
        path = "./picture/" + (std::string)fileInfo["FileName"];
    }

    uint64_t userID = BusinessNetWork::getInstance().getUserID(channel);

    SyncPoint point = std::get<int64_t>(converter.getHeaderField(4));
    DEBUG_INFO("new file: " +  path + " size: " + std::to_string((uint64_t)fileInfo["FileSize"]));

    // 如果当前要上传到服务器上的文件已经存在了，或者该用户正在上传一个其他的文件，则拒绝本次请求

    if (BusinessSQL::getInstance().queryAFileExists(fileInfo["FileName"], type) || checkDownload(userID)) {
        // exists
        // reject
        BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                           4, 4, 0, 0, point,
                                                           nullptr, 0);
        return ;
    }
    // not exists , accept
    FileManager::getInstance().registerID(id, path, fileInfo["FileSize"]);
    updateDownloadID(userID, id);

    BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                       4, 3, 0, 0, point, nullptr, 0);
}

void BusinessFile::recvFileBlock(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel)
{
    FileManager& fileManager = FileManager::getInstance();
    nlohmann::json dataJson = getJsonOfMessage(protocol);

    fileManager.commitToQueue(dataJson);

    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());

    SyncPoint point = std::get<int64_t>(converter.getHeaderField(4));

    BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                       5, 2, 0, 0, point, nullptr, 0);
}

void BusinessFile::recvEndFileTransmission(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel)
{
    // 1.upload file info to database
    // 2.erase fileManager
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());
    // get file id
    uint64_t transmissionID = std::get<int64_t>(converter.getHeaderField(4));
    uint64_t userID = BusinessNetWork::getInstance().getUserID(channel);
    // get file name with filePath
    FileManager& fileManager = FileManager::getInstance();
    std::string path = fileManager.queryFilePath(transmissionID);
    int type = 0;
    if (path[2] == 'f') type = 1;
    else type = 2;

    auto k = path.find_last_of('/');
    std::string fileName = path.substr(k + 1);
    DEBUG_INFO(fileName);
    // upload filename
    BusinessSQL& sql = BusinessSQL::getInstance();
    sql.insertAFile(fileName, fileManager.queryFileSize(transmissionID), type);
    // erase fileManager
    fileManager.finishID(transmissionID);
    updateDownloadID(userID, -1);
}

nlohmann::json BusinessFile::getJsonOfMessage(std::shared_ptr<Protocol> protocol)
{
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());
    std::string jsonStr;
    jsonStr.resize(converter.getParsedDataSize(), '\0');
    converter.copyParsedData(jsonStr.data());
    return nlohmann::json::parse(jsonStr);
}


void BusinessFile::eraseUserTransfer(uint64_t userID)
{
    auto& it = userTransferTaskIds[userID];
    if (checkUpload(userID)) {
        // 如果当前有上传的任务
        uint64_t syncPoint = it.uploadSyncPoint;
        // 停止上传
        it.uploadSyncPoint = -1;
        // 结束同步任务
        SyncManager::getInstance().releaseSyncPoint(syncPoint, false);
    }

    if (checkDownload(userID)) {
        // 如果当前有下载的任务
        uint64_t transmissionID = it.downloadTaskId;
        // 结束下载
        it.downloadTaskId = -1;
        // 清空文件管理器中的任务
        FileManager::getInstance().finishID(transmissionID);
    }
    userTransferTaskIds.erase(userID);
}

void BusinessFile::updateDownloadID(uint64_t userID, uint64_t id)
{
    std::lock_guard<std::mutex> guard(storeLock);
    auto& it = userTransferTaskIds[userID];
    it.downloadTaskId = id;
}

void BusinessFile::updateUploadSyncPoint(uint64_t userID, uint64_t point)
{
    std::lock_guard<std::mutex> guard(storeLock);
    auto& it = userTransferTaskIds[userID];
    it.uploadSyncPoint = point;
}

bool BusinessFile::checkUpload(uint64_t userID)
{
    std::lock_guard<std::mutex> guard(storeLock);
    auto& it = userTransferTaskIds[userID];
    if (it.uploadSyncPoint == -1) return false;
    return true;
}

bool BusinessFile::checkDownload(uint64_t userID)
{
    std::lock_guard<std::mutex> guard(storeLock);
    auto& it = userTransferTaskIds[userID];
    if (it.downloadTaskId == -1) return false;
    return true;
}
